const jwt = require('jsonwebtoken');
const User = require('../models/User');
const authConfig = require('../config/auth');
const { BadRequestError, UnauthorizedError, ForbiddenError } = require('../utils/errors');

/**
 * 认证服务类
 * 处理用户登录、注册、令牌管理等认证相关功能
 */
class AuthService {
  /**
   * 生成JWT令牌
   * @param {Object} user - 用户对象
   * @returns {Object} 包含accessToken和refreshToken的对象
   */
  static generateTokens(user) {
    // 生成访问令牌
    const accessToken = jwt.sign(
      {
        id: user.id,
        username: user.username,
        role: user.role,
        email: user.email
      },
      authConfig.jwt.secret,
      {
        expiresIn: authConfig.jwt.expiresIn,
        issuer: authConfig.jwt.issuer,
        audience: authConfig.jwt.audience
      }
    );

    // 生成刷新令牌
    const refreshToken = jwt.sign(
      {
        id: user.id,
        tokenType: 'refresh'
      },
      authConfig.jwt.secret,
      {
        expiresIn: authConfig.jwt.refreshExpiresIn,
        issuer: authConfig.jwt.issuer,
        audience: authConfig.jwt.audience
      }
    );

    return {
      accessToken,
      refreshToken,
      expiresIn: authConfig.jwt.expiresIn
    };
  }

  /**
   * 验证JWT令牌
   * @param {string} token - JWT令牌
   * @returns {Object} 解码后的令牌数据
   */
  static verifyToken(token) {
    try {
      return jwt.verify(token, authConfig.jwt.secret, {
        issuer: authConfig.jwt.issuer,
        audience: authConfig.jwt.audience
      });
    } catch (error) {
      if (error.name === 'TokenExpiredError') {
        throw new UnauthorizedError('令牌已过期');
      } else if (error.name === 'JsonWebTokenError') {
        throw new UnauthorizedError('无效的令牌');
      } else {
        throw new UnauthorizedError('令牌验证失败');
      }
    }
  }

  /**
   * 刷新令牌
   * @param {string} refreshToken - 刷新令牌
   * @returns {Object} 新的访问令牌和刷新令牌
   */
  static async refreshTokens(refreshToken) {
    try {
      // 验证刷新令牌
      const decoded = jwt.verify(refreshToken, authConfig.jwt.secret, {
        issuer: authConfig.jwt.issuer,
        audience: authConfig.jwt.audience
      });

      // 检查令牌类型
      if (decoded.tokenType !== 'refresh') {
        throw new UnauthorizedError('无效的刷新令牌');
      }

      // 查找用户
      const user = await User.findByPk(decoded.id);
      if (!user) {
        throw new UnauthorizedError('用户不存在');
      }

      // 检查用户状态
      if (user.status !== 1) {
        throw new ForbiddenError('用户账户已被禁用');
      }

      // 生成新的令牌
      return this.generateTokens(user);
    } catch (error) {
      if (error.name === 'TokenExpiredError') {
        throw new UnauthorizedError('刷新令牌已过期');
      } else if (error.name === 'JsonWebTokenError') {
        throw new UnauthorizedError('无效的刷新令牌');
      } else {
        throw error;
      }
    }
  }

  /**
   * 用户登录
   * @param {string} usernameOrEmail - 用户名或邮箱
   * @param {string} password - 密码
   * @param {string} ipAddress - 登录IP地址
   * @returns {Object} 登录结果，包含用户信息和令牌
   */
  static async login(usernameOrEmail, password, ipAddress = null) {
    // 验证输入
    if (!usernameOrEmail || !password) {
      throw new BadRequestError('用户名/邮箱和密码不能为空');
    }

    // 查找用户
    const user = await User.findOne({
      where: {
        [require('sequelize').Op.or]: [
          { username: usernameOrEmail },
          { email: usernameOrEmail }
        ]
      }
    });

    if (!user) {
      throw new UnauthorizedError('用户名/邮箱或密码错误');
    }

    // 检查用户状态
    if (user.status !== 1) {
      throw new ForbiddenError('用户账户已被禁用');
    }

    // 检查账户是否被锁定
    if (user.isLocked()) {
      const remainingTime = Math.ceil((user.lock_until - new Date()) / 1000 / 60);
      throw new ForbiddenError(`账户已被锁定，请${remainingTime}分钟后再试`);
    }

    // 验证密码
    if (!user.verifyPassword(password)) {
      // 增加登录失败次数
      await user.incrementLoginAttempts();
      throw new UnauthorizedError('用户名/邮箱或密码错误');
    }

    // 重置登录失败次数并记录IP
    user.last_login_ip = ipAddress;
    await user.resetLoginAttempts();

    // 生成令牌
    const tokens = this.generateTokens(user);

    // 返回用户信息和令牌（不包含敏感信息）
    return {
      user: {
        id: user.id,
        username: user.username,
        email: user.email,
        role: user.role,
        nickname: user.nickname,
        avatar: user.avatar,
        status: user.status,
        lastLoginAt: user.last_login_at
      },
      tokens
    };
  }

  /**
   * 用户注册
   * @param {Object} userData - 用户注册数据
   * @returns {Object} 注册结果
   */
  static async register(userData) {
    const { username, email, password, nickname, avatar } = userData;

    // 验证输入
    if (!username || !email || !password) {
      throw new BadRequestError('用户名、邮箱和密码不能为空');
    }

    // 密码强度检查
    if (password.length < 6) {
      throw new BadRequestError('密码长度至少为6个字符');
    }

    // 检查用户名是否已存在
    const existingUser = await User.findOne({
      where: {
        [require('sequelize').Op.or]: [
          { username },
          { email }
        ]
      }
    });

    if (existingUser) {
      if (existingUser.username === username) {
        throw new BadRequestError('用户名已被使用');
      } else {
        throw new BadRequestError('邮箱已被注册');
      }
    }

    // 创建用户
    const newUser = await User.create({
      username,
      email,
      password,
      nickname: nickname || username,
      avatar: avatar || null,
      role: 'user' // 注册用户默认为普通用户
    });
    
    // 确保avatar字段被正确保存
    console.log('新用户创建，头像字段:', newUser.avatar);

    // 生成令牌
    const tokens = this.generateTokens(newUser);

    return {
      user: {
        id: newUser.id,
        username: newUser.username,
        email: newUser.email,
        role: newUser.role,
        nickname: newUser.nickname,
        avatar: newUser.avatar,
        status: newUser.status
      },
      tokens
    };
  }

  /**
   * 获取用户权限
   * @param {string} role - 用户角色
   * @returns {Array} 权限列表
   */
  static getUserPermissions(role) {
    return authConfig.rolePermissions[role] || [];
  }

  /**
   * 检查用户是否有指定权限
   * @param {string} role - 用户角色
   * @param {string} permission - 权限名称
   * @returns {boolean} 是否有权限
   */
  static hasPermission(role, permission) {
    const permissions = this.getUserPermissions(role);
    return permissions.includes(permission);
  }

  /**
   * 注销令牌（将令牌加入黑名单）
   * @param {string} token - 要注销的令牌
   * @returns {Promise<void>}
   * @description 注意：此功能需要令牌黑名单存储机制，当前仅提供接口
   */
  static async revokeToken(token) {
    // 在实际项目中，这里应该将令牌加入黑名单
    // 可以使用Redis或其他缓存机制存储黑名单
    console.log(`令牌已注销: ${token.substring(0, 20)}...`);
    
    // 实际实现示例（需要Redis）:
    // const decoded = this.verifyToken(token);
    // const expiryTime = decoded.exp - Math.floor(Date.now() / 1000);
    // await redisClient.set(`blacklist:${token}`, '1', 'EX', expiryTime);
  }
  
  /**
   * 生成密码重置令牌
   * @param {string} email - 用户邮箱
   * @returns {Promise<Object>} 重置令牌和过期时间
   */
  /**
   * 请求密码重置
   * @param {string} email - 用户邮箱
   * @returns {Promise<void>}
   */
  static async requestPasswordReset(email) {
    // 生成重置令牌
    const { resetToken, expiresAt } = await this.generatePasswordResetToken(email);
    
    // 在实际项目中，这里应该发送邮件给用户
    // 包含重置链接，如: https://example.com/reset-password?token=xxx
    console.log(`密码重置令牌已生成: ${resetToken}`);
    console.log(`重置链接有效期至: ${expiresAt}`);
    
    // 实际邮件发送示例（需要邮件服务）:
    // await emailService.sendPasswordResetEmail(email, resetToken);
  }

  /**
   * 生成密码重置令牌
   * @param {string} email - 用户邮箱
   * @returns {Promise<Object>} 重置令牌和过期时间
   */
  static async generatePasswordResetToken(email) {
    // 查找用户
    const user = await User.findOne({ where: { email } });
    if (!user) {
      throw new BadRequestError('该邮箱未注册');
    }
    
    // 生成随机令牌
    const crypto = require('crypto');
    const resetToken = crypto.randomBytes(32).toString('hex');
    const hashedToken = require('../utils/password').hashPassword(resetToken);
    
    // 设置令牌和过期时间（1小时）
    user.passwordResetToken = hashedToken;
    user.passwordResetExpires = new Date(Date.now() + 3600000); // 1小时后过期
    
    await user.save();
    
    return {
      resetToken,
      expiresAt: user.passwordResetExpires
    };
  }
  
  /**
   * 重置密码
   * @param {string} resetToken - 重置令牌
   * @param {string} newPassword - 新密码
   * @returns {Promise<Object>} 重置结果
   */
  static async resetPassword(resetToken, newPassword) {
    // 验证新密码
    if (!newPassword || newPassword.length < 6) {
      throw new BadRequestError('密码长度至少为6个字符');
    }
    
    // 查找用户
    const user = await User.findByPasswordResetToken(resetToken);
    if (!user) {
      throw new UnauthorizedError('无效的或已过期的重置令牌');
    }
    
    // 更新密码
    user.password = newPassword;
    user.passwordResetToken = null;
    user.passwordResetExpires = null;
    
    await user.save();
    
    // 生成新令牌
    const tokens = this.generateTokens(user);
    
    return {
      user: {
        id: user.id,
        username: user.username,
        email: user.email,
        role: user.role,
        nickname: user.nickname
      },
      tokens
    };
  }
}

module.exports = AuthService;

// 导出到全局
global.AuthService = AuthService;